package com.heima.admin.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.admin.mapper.AdChannelMapper;
import com.heima.admin.mapper.AdSensitiveMapper;
import com.heima.admin.service.WemediaNewsAutoScanService;
import com.heima.aliyun.GreenImageScan;
import com.heima.aliyun.GreenTextScan;
import com.heima.common.constants.message.ArticleForEsConstants;
import com.heima.common.exception.CustException;
import com.heima.feigns.ArticleFeign;
import com.heima.feigns.WemediaFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.utils.common.SensitiveWordUtil;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
public class WemediaNewsAutoScanServiceImpl implements WemediaNewsAutoScanService {
    @Autowired
    WemediaFeign wemediaFeign;
    @Autowired
    GreenTextScan greenTextScan;
    @Autowired
    GreenImageScan greenImageScan;
    @Value("${file.oss.web-site}")
    private String webSite;
    @Autowired
    KafkaTemplate<String,String> kafkaTemplate;
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void autoScanByMediaNewsId(Integer wmNewsId) {
        //1.检查参数
        if (wmNewsId == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.调用feign接口查询自媒体文章信息
        ResponseResult<WmNews> result = wemediaFeign.findById(wmNewsId);
        WmNews wmNews = result.getData();
        if (wmNews == null) {
            log.error("审核的自媒体文章不存在，自媒体的id:{}", wmNewsId);
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3.检查文章状态，状态为4（人工审核通过）直接保存数据和创建索引
        Short status = wmNews.getStatus();
        if (status == WmNews.Status.ADMIN_SUCCESS.getCode() && wmNews.getPublishTime().getTime() <= System.currentTimeMillis()) {
            //保存数据
            publishArticle(wmNews);
            return;
        }
        //4.状态为8发布时间小于当前时间 直接保存数据
        if (status == WmNews.Status.SUCCESS.getCode() && wmNews.getPublishTime().getTime() <= System.currentTimeMillis()) {
            //保存数据
            publishArticle(wmNews);
            return;
        }
        //5.文章状态=1 待审核
        if (status == WmNews.Status.SUBMIT.getCode()) {
            // 抽取文章内容中的文本和图片
            Map<String, Object> contentAndImagesResult = handleTextAndImages(wmNews);
            //4.1 自管理敏感词审核
            boolean isSensitiveScan = handleSensitive((String)contentAndImagesResult.get("content"),wmNews);
            if (!isSensitiveScan) return;
            // 4.2 阿里云审核文本内容
            boolean isTextScan = handleTextScan((String) contentAndImagesResult.get("content"), wmNews);
            if (!isTextScan) return;
            // 4.3 阿里云图片审核
            boolean isImageScan = handleImagesScan((List) contentAndImagesResult.get("images"), wmNews);
            if (!isImageScan) return;
            //判断文章发布时间大于当前时间，修改状态8
            if (wmNews.getPublishTime().after(new Date())) {
                updateWmNews(wmNews, WmNews.Status.SUCCESS.getCode(), "文章审核通过，待发布");
                return;
            }
            //保存文章相关数据
            publishArticle(wmNews);
            //同步索引库
            //创建索引（为后续app端的搜索功能做数据准备）
            Map map = new HashMap();
            map.put("articleId",wmNews.getArticleId());
            kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_ADD,JSON.toJSONString(map));

        }


    }

    private boolean handleImagesScan(List<String> images, WmNews wmNews) {
        boolean flag = true;
        // 无图则无需审核
        if(images==null || images.size() == 0){
            return true;
        }
        try {
            //去重
            images = images.stream().distinct().collect(Collectors.toList());
            Map map = greenImageScan.imageUrlScan(images);
            String suggestion = (String) map.get("suggestion");
            switch (suggestion) {
                case "block":
                    flag = false;
                    updateWmNews(wmNews,WmNews.Status.FAIL.getCode(),"图片中内容有敏感词汇");
                    break;
                case "review":
                    flag = false;
                    updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"图片中内容需要人工审核");
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文章阿里云内容审核失败，转入人工审核, e：{}",e.getMessage());
            flag = false;
            updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH.getCode(), "文章阿里云内容审核失败,转入需要人工审核");
        }
        return flag;
    }

    private boolean handleTextScan(String content, WmNews wmNews) {
        boolean flag = true;
        try {
            Map map = greenTextScan.greenTextScan(content);
            String suggestion = (String) map.get("suggestion");
            switch (suggestion) {
                case "block":
                    flag = false;
                    updateWmNews(wmNews,WmNews.Status.FAIL.getCode(),"文本中内容有敏感词汇");
                    break;
                case "review":
                    flag = false;
                    updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"文本中内容需要人工审核");
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文章阿里云内容审核失败，转入人工审核, e：{}",e.getMessage());
            flag = false;
            updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH.getCode(), "文章阿里云内容审核失败,转入需要人工审核");
        }
        return flag;
    }

    @Autowired
    AdSensitiveMapper adSensitiveMapper;
    /**
     * 自管理敏感词审核
     * @param content
     * @param wmNews
     * @return
     */
    private boolean handleSensitive(String content, WmNews wmNews) {
        boolean flag = true;
        //查询数据库所有的敏感词
        List<String> allSensitive = adSensitiveMapper.findAllSensitive();
        //把敏感词转换成为dfa格式的敏感词
        SensitiveWordUtil.initMap(allSensitive);
        //检测文本中的内容有无匹配到敏感词
        Map<String, Integer> map = SensitiveWordUtil.matchWords(content);
        if (map.size()>0) {
            flag = false;
            updateWmNews(wmNews, (short)2,"文章中包含了敏感词:"+map);
        }
        return flag;
    }
    private void updateWmNews(WmNews wmNews, short status, String reason) {
        wmNews.setStatus(status);
        wmNews.setReason(reason);
        ResponseResult responseResult = wemediaFeign.updateWmNews(wmNews);
        if(responseResult.getCode().intValue()!=0){
            log.error("远程调用 updateWmNews 方法失败  参数:{}",wmNews);
            CustException.cust(AppHttpCodeEnum.SERVER_ERROR,responseResult.getErrorMessage());
        }
    }
    @Autowired
    ArticleFeign articleFeign;
    private void publishArticle(WmNews wmNews) {
        //1. 远程调用saveArticle 保存3张表的信息
        Long articleId = saveArticle(wmNews);
        //2. 修改自媒体文章状态  9
        wmNews.setArticleId(articleId);
        updateWmNews(wmNews,(short) 9 ,"发布成功");
    }
    @Autowired
    AdChannelMapper adChannelMapper;
    /**
     * 远程调用feign保存三张表信息
     * @param wmNews
     * @return
     */
    private Long  saveArticle(WmNews wmNews) {
        //1. 封装dto参数
        ArticleDto articleDto = new ArticleDto();
        BeanUtils.copyProperties(wmNews,articleDto);
        // 如果之前没发布过  id为null 发布过为之前关联的articleId
        articleDto.setId(wmNews.getArticleId());
        articleDto.setLayout(wmNews.getType()); // 布局
        // 设置频道信息
        if (wmNews.getChannelId()!=null) {
            AdChannel adChannel = adChannelMapper.selectById(wmNews.getChannelId());
            articleDto.setChannelName(adChannel.getName());
        }
        // 0 普通文章
        articleDto.setFlag((byte) 0);
        // 自媒体用户id 用于在article服务中查询相关作者信息
        articleDto.setWmUserId(wmNews.getUserId());
        // 远程调用 article微服务保存3张表方法
        ResponseResult<Long> saveResult = articleFeign.saveArticle(articleDto);
        if(saveResult.getCode()!=0){
            log.error("远程调用 保存文章 方法失败  参数:{}",articleDto);
            CustException.cust(AppHttpCodeEnum.SERVER_ERROR,saveResult.getErrorMessage());
        }
        return saveResult.getData();
    }

    private Map<String, Object> handleTextAndImages(WmNews wmNews) {
        //1 获取文章内容并转换对象
        String content = wmNews.getContent();
        if (StringUtils.isBlank(content) && content.length() > 10000) {
            log.error("文章内容 校验错误 为空 或 长度大于10000");
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章内容不合法");
        }
        List<Map> list = JSON.parseArray(content, Map.class);
        //2 解析内容文本 Stream 流优化 --> String
        // 中美
        // 国家英语   美_hmtt_国？
        String text = list.stream().filter(map -> "text".equals(map.get("type")))
                .map(map -> map.get("value").toString())
                .collect(Collectors.joining("_hmtt_"));
        // 文章标题 参与审核
        text = text + "_hmtt_" + wmNews.getTitle();
        //3 解析内容图片
        List<String> images = list.stream().filter(map -> "images".equals(map.get("type")))
                            .map(map -> map.get("value").toString())
                            .collect(Collectors.toList());
            //4 处理封面前缀 1.jpg 2.jpg
            String imageCover = wmNews.getImages();
            if (StringUtils.isNotBlank(imageCover)) {
                // 添加到内容图片列表
                images.addAll(
                        Stream.of(imageCover.split(","))
                                .map(x -> webSite + x)
                                .collect(Collectors.toList())
            );
        }
        // 添加到内容图片列表
        //5 封装结果
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("content", text);
        resultMap.put("images", images);
        return resultMap;
    }
}
